iT邦幫忙

第 12 屆 iThome 鐵人賽

DAY 3
0

這篇我們介紹 observer 模式。本篇會涵蓋:

  • 簡介 observer 模式
  • 案例:電子商務的新需求
  • Observer 模式關鍵特徵
  • 與其他模式的合用

介紹 observer 模式

GoF 如此描述 observer 模式:

定義物件間的一種一對多的相依關係,當一個物件的狀態發生改變時,所有相依於它的物件都將得到通知自動更新。

這個模式是當某事件發生時,某些物件同時需要得到通知,而這通知是希望自動發出。這模式能將通知者和被通知者解耦。

這個模式很常被使用到,又被稱為相依(Dependents)或發佈—訂閱(Publish-Subscribe)。

案例:電子商務的新需求

接下來,我們提一個案例來研究。我們一樣來看 Day10 中電子商務的例子。

新需求:發送歡迎信和確認消費者資訊

我們希望這個系統在消費者進入系統時,做以下兩個操作:

  • 向消費者發送一封歡迎信
  • 向郵局查證消費者的地址

不好的做法:Hard-code 寫進 Customer 物件

首先我們應該都同意我們會先製作一個消費者的物件(Customer),接下來就思考我們如何解決上述的問題。

其中一種解決方案是直接把以上兩個程序 hard-coded 進到 Customer 物件。但是這樣很顯然地會使程式非常不靈活且僵硬,上述的行為與 Customer 的耦合性也高,一但有了新的需求或是一些原因要移除上述的需求(根據時代演進,商業模型有所變化),維護這段程式碼會十分痛苦。

引入 observer 模式—— 4 步驟

基於上述慘痛的方案,我們來看看 observer 模式是否能有更好的解法。在本案例中,我們實踐 observer 模式有四步驟:

Step1

第一步驟是我們要找出所有希望接收到通知的物件,他們被稱作 Observer;而本例中, Customer 物件被稱作 Subject。

而且我們希望所有 Observers 是相同的類型(共享同一個介面)。

Step2

第二步驟是:我們需要有一種方式使 Observers 知道自己要觀察哪些 Subject,因此我們要有一個註冊方式可以讓它們註冊自己。我們將在 Subject 中新增兩個 methods:

  • attach(Observer):將指定的 Observer 增加到 Subject 的觀察者清單中
  • detach(Observer):從 Subject 的觀察者清單中刪除指定的 Observer

Step3

事件發生時,Subject 會通知已經註冊的 Observer 們。因此我們需要為每個 Observer 實作一個 update() method。

Subject 將實作一個 notify() 來 run through 所有已註冊的 Observer,並且呼叫每個 Observer 物件的 update()

Step4

除了通知 Observer 之外,我們也可能需要給予該事件的更多資訊。我們需要在 Subject 實作一些 method 來給其他 Observer 可能需要的資訊,再可以透過 update() 傳遞給 Observer。

以下是 UML 圖:

程式碼實踐

我們將用 Java 實作以上步驟:

import java.util.*;

class Customer {
  	static private Vector myObservers;
  	static {
      	myObservers = new Vector();
    }
  	
  	public static void attach(Observer o) {
      	// 註冊
      	myObservers.addElement(o);
    }
  	public static void detach(Observer o) {
      	myObservers.remove(o);
    }
  	public String getState() {
      	// 以一些方式取得資訊,在此實作先回傳 null
      	return null;
    }
  	public void notify() {
      	// 進行一些設定,讓 observers 知道發生什麼事
      	for(Enumeration e = myObservers.elements();
            e.hasMorElements(); ) {
          			((Observer) e).update(this);
        }
    }
}

interface Observer {
  	void update(Customer myCust);
}

class AddrVerification implements Observer {
  	public AddrVerification() {
    }
  	public void update(Customer myCust) {
    		// 透過 myCust 驗證地址
      	// 取得相關客戶更多訊息
    }
}

class WelcomeLetter implements Observer {
  	public WelcomeLetter() {
    }
  	public void update(Customer myCust) {
    		// 處理寄歡迎信的操作
      	// 透過 myCust 取得資訊
    }
}

Observer 模式的關鍵特徵

以下是本模式的關鍵特徵:

項目 內容
意圖 在物件之間定義一種一對多的相依關係,如此當一個物件的狀態改變時,所有希鴦者都將得到通知並自動更新
問題 當某個事件發生時,需要像一系列變化著的物件發出通知
解決方案 Observer 將監視某個事件的責任委託給 Subject
參與者與協作者 Subject 知道自己的 Observer,因為 Observer 要向它註冊。Subject 必須在所監視的始建發生時通知 ObserverObserver 負責向 Subject 註冊,以及在得到通知時從 Subject 處獲取資訊
效果 如果某些 Observer 只對事件的一個子集感興趣,那麼 Subject 可能會告訴它們不需要知道的事件。如果 Subject 通知 ObserverObserver 還返回請求更多資訊,則可能需要額外的通訊
實作 讓某個事件發生需要知道的物件將自己註冊到另一個監視事件發生或自己觸發事件的物件上。

以下是本模式的 UML 圖:

與其他模式的合用

與 adapter 模式

回頭看剛剛的例子,如果 WelcomeLetterAddrVerification 是已經存在的物件,那麼我們就會使用 adapter 模式讓它放進同一個共用介面中。

與 strategy 模式

一個 Observer 可能只需要處理事件的某些情況。在這種情況下我們可能需要將額外的通知篩選掉。

將篩選通知到責任轉給 Subject 物件,可以避免額外的通知。我們可以在此讓 Subject 物件使用 strategy 模式決定通知是否該發出。每個 Observer 註冊時都將正確的 Strategy 物件提供給 Subject 物件。

接下來

下篇我們會提到 Template Method 模式。ㄅㄅ!


上一篇
DAY16: Decorator 模式
下一篇
DAY18: Template Method 模式
系列文
來讀設計模式:Junior developer 跟大家一起練功22
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言